package com.shift.sdk;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.os.Handler;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import com.shift.ccsdk.CCBasePoint;
import com.shift.ccsdk.CCDetail;
import com.shift.ccsdk.CCSDK;
import com.shift.sdk.R;

/**
 * カメラプレビュー(リアルタイム)
 *
 * @author SHIFT
 */
public class MoviePreview implements AutoFocusCallback,Camera.PreviewCallback,
SurfaceHolder.Callback, Camera.ErrorCallback
{
    /**
     * 解析画像表示用サーフェイス.
     */
//    private SurfaceView analyzeImageSurface;

    /**
     * カメラ画像表示用サーフェイス.
     */
    private SurfaceView cameraImageSurface;

    /**
     * 解析画像表示用サーフェイスホルダー.
     */
    private SurfaceHolder cameraImageSurfaceHolder;

    /**
     * カメラ画像表示用サーフェイスホルダー.
     */
//    private SurfaceHolder analyzeImageSurfaceHolder;

    /**
     * カメラ.
     */
    protected Camera mCamera;

    /**
     * コンテキスト(アクティビティ).
     */
    private MovieActivity activity;

    /**
     * チェック状態を保持するマップ.
     */
//    private Map<Long, Boolean> checkedCcIdMap;

    /**
     * 解析結果リストのアダプタ.
     */
    public CCIdListAdapter adapter;

    /**
     * カメラのプレビューサイズ.
     */
    private int previewWidth = 0;

    /**
     * カメラのプレビューサイズ.
     */
    private int previewHeight = 0;

    /**
     * 画面上のカメラプレビューサイズ（ピクセル）.
     */
    private final int PREVIEW_WINDOW_SIZE = 1;
    
    public List<CCInfo> resultlist;
    
    public Handler handler = new Handler();
    
    public boolean isScanning = true;
    
    public int index = 1;

    /**
     * サーフェイスの設定を行う
     */
    public void setSurfaceView(MovieActivity activity,SurfaceView analyzeImageSurface, SurfaceView cameraImageSurface,
            int analyzeImageWidth, int analyzeImageHeight) {

//        this.analyzeImageSurface = analyzeImageSurface;
        this.cameraImageSurface = cameraImageSurface;

        cameraImageSurfaceHolder = cameraImageSurface.getHolder();
        cameraImageSurfaceHolder.addCallback(this);
        this.activity = activity;
        //レイアウトサイズを変更
        //90度回転するため幅と高さを逆にする
        previewWidth = analyzeImageWidth;
        previewHeight = analyzeImageHeight;
    }

    /**
     * SurfaceView生成時の処理
     */
    @Override
    public void surfaceCreated(SurfaceHolder surfaceholder) {
        try {
            mCamera = Camera.open();
            mCamera.setPreviewDisplay(cameraImageSurface.getHolder());
            mCamera.setErrorCallback(this);
            // カメラの向きを縦に設定
//            checkedCcIdMap = new LinkedHashMap<Long, Boolean>();

            // 空の解析結果リスト生成
            resultlist = new ArrayList<CCInfo>();
            adapter = new CCIdListAdapter(activity, R.layout.list_low, resultlist);
            ListView ccIdList = (ListView) (activity).findViewById(R.id.ccIdList);
            ccIdList.setAdapter(adapter);
            ccIdList.setItemsCanFocus(false);
//            ccIdList.setOnItemClickListener(this);
            adapter.notifyDataSetChanged();

        } catch (Exception e) {

            if (mCamera != null) {
                mCamera.release();
                mCamera = null;
            }
            Log.e(activity.getPackageName(), e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * ビューが変化した際の処理.
     */
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        // プレビュー停止
        mCamera.stopPreview();

        // カメラ情報取得
        Parameters mParam = mCamera.getParameters();

        // カメラがサポートするプレビューサイズを取得
        int tmpWidth = 0;
        int tmpHeight = 0;
        for (Size size : mParam.getSupportedPreviewSizes()) {
            if (size.width > previewWidth || size.height > previewHeight) {
                continue;
            }

            if (tmpWidth <= size.width) {
                if (tmpHeight < size.height) {
                    tmpWidth = size.width;
                    tmpHeight = size.height;
                }
            }
        }

        previewWidth = tmpWidth;
        previewHeight = tmpHeight;

        // プレビューサイズをカメラのパラメータにセット
        mParam.setPreviewSize(previewWidth, previewHeight);

        RelativeLayout.LayoutParams layoutParams = (android.widget.RelativeLayout.LayoutParams) cameraImageSurface.getLayoutParams();
        layoutParams.height = previewHeight;
        layoutParams.width = previewWidth;
        cameraImageSurface.setLayoutParams(layoutParams);
        activity.rectView.setLayoutParams(layoutParams);

        mCamera.setParameters(mParam);

        // プレビュー開始
        PixelFormat pixcelInfo = new PixelFormat();
        PixelFormat.getPixelFormatInfo(mCamera.getParameters().getPreviewFormat(), pixcelInfo);
        byte[] buffer = new byte[mCamera.getParameters().getPreviewSize().width *
                                 mCamera.getParameters().getPreviewSize().height *
                                 pixcelInfo.bitsPerPixel / 8];
        mCamera.addCallbackBuffer(buffer);
        mCamera.setPreviewCallbackWithBuffer(this);
        mCamera.startPreview();
        
        handler.postDelayed(new  FocusThread(mCamera), 2000);
    }

    /**
     * サーフェイスビュー破棄時の処理.
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceholder) {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    /**
     * プレビュー開始.
     */
    public void startPreview() {
        if (mCamera != null) {
            PixelFormat pixcelInfo = new PixelFormat();
            PixelFormat.getPixelFormatInfo(mCamera.getParameters().getPreviewFormat(), pixcelInfo);
            byte[] buffer = new byte[mCamera.getParameters().getPreviewSize().width *
                                     mCamera.getParameters().getPreviewSize().height *
                                     pixcelInfo.bitsPerPixel / 8];
            mCamera.addCallbackBuffer(buffer);
            mCamera.setPreviewCallbackWithBuffer(this);
            mCamera.startPreview();
        }
    }

    /**
     * プレビュー終了.
     */
    public void stopPreview() {
        if (mCamera != null) {
            mCamera.addCallbackBuffer(null);
            mCamera.setPreviewCallbackWithBuffer(null);
            mCamera.stopPreview();
            isScanning = false;
        }
    }

    /**
     * プレビューのコールバック.
     */
    @Override
	public void onPreviewFrame(byte[] data, Camera camera) {
		if (mCamera == null) {
			return;
		}
		int tmpWidth = mCamera.getParameters().getPreviewSize().width;
		int tmpHeight = mCamera.getParameters().getPreviewSize().height;
		
//		int tmpWidth = cameraImageSurface.getLayoutParams().width;
//		int tmpHeight = cameraImageSurface.getLayoutParams().height;

		// カメレオンコード解析
		int[] array = new int[tmpWidth * tmpHeight];
		List<CCDetail> analyzeResult = CCSDK.AnalyzeYuv(data, tmpWidth,
				tmpHeight, MovieActivity.option.getMaxCodeCount(),
				MovieActivity.option.getAnalizeLevel(), array);

		// 基準点を取得
		List<CCBasePoint> basePointList = CCSDK.getCCPointBase();
		
		// チェックされているCCIDを抽出
		List<CCDetail> selectedAnalyzeResultList = new ArrayList<CCDetail>();
		List<CCBasePoint> selectedBasePointList = new ArrayList<CCBasePoint>();
		for (int i = 0; i < analyzeResult.size(); i++) {
			selectedAnalyzeResultList.add(analyzeResult.get(i));
			selectedBasePointList.add(basePointList.get(i));
		}
		int[] newArray = null;
		// 基準点描画オプションが設定されている場合
		if (MovieActivity.option.isDrawBasePoint()) {
			// カメレオンコードの枠線と基準点を描画
			newArray = CCSDK.DrawCodeAreaPoint(array, tmpWidth, tmpHeight,
					selectedAnalyzeResultList, selectedBasePointList,
					MovieActivity.option.getThickness());
		} else {
			// カメレオンコードの枠線を描画
			newArray = CCSDK.DrawCodeArea(array, tmpWidth, tmpHeight,
					selectedAnalyzeResultList,
					MovieActivity.option.getThickness());
		}
		
		// int[]をBimapに変換
		Bitmap tmpBitmap = Bitmap.createBitmap(tmpWidth, tmpHeight,
				Config.ARGB_8888);
		tmpBitmap.setPixels(newArray, 0, tmpWidth, 0, 0, tmpWidth, tmpHeight);
		
		List<CCDetail> lc = new LinkedList<CCDetail>();
		// 解析結果リストの更新
//		adapter.clear();
		for (int i = 0; i < analyzeResult.size(); i++) {
			long ccId = analyzeResult.get(i).ccId;
			CCInfo info = new CCInfo();
			info.setNumber(String.valueOf(index));
			info.setCcId(String.valueOf(ccId));
			info.setChecked(true);
			if(adapter.addItem(info)){
				lc.add(analyzeResult.get(i));
			}
		}
		activity.drawRect(lc,tmpWidth,tmpHeight);
		adapter.notifyDataSetChanged();

		mCamera.autoFocus(this);
	}

    /**
     * カメラエラー発生時のコールバック
     * @param error エラーコード
     * @param camera カメラサービスオブジェクト
     */
    @Override
    public void onError(int error, Camera camera) {
        Log.e(activity.getPackageName(), "Error Code : " + error);
        if(error == Camera.CAMERA_ERROR_SERVER_DIED){
            try {
                //プレビューの停止
                stopPreview();

                //カメラの解放
                mCamera.release();

                //カメラの再取得及びコールバック再設定
                mCamera = Camera.open();
                mCamera.setPreviewDisplay(cameraImageSurface.getHolder());
                mCamera.setErrorCallback(this);

                //カメラのパラメータを設定
                mCamera.setDisplayOrientation(90);
                Parameters mParam = mCamera.getParameters();
                // プレビューサイズをカメラのパラメータにセット
                mParam.setPreviewSize(previewWidth, previewHeight);
                mCamera.setParameters(mParam);

                //プレビュー再開
                startPreview();

            } catch (IOException e) {

                if (mCamera != null) {
                    mCamera.release();
                    mCamera = null;
                }
                Log.e(activity.getPackageName(), e.getMessage());
                e.printStackTrace();
            }
        }
    }

	@Override
	public void onAutoFocus(boolean success, Camera camera) {
			camera.setOneShotPreviewCallback(this);
			handler.postDelayed(new  FocusThread(camera), 1000);
	}
	
	class FocusThread implements Runnable{
		
		Camera c;
		
		public FocusThread(Camera c){
			this.c = c;
		}
		@Override
		public void run() {
			if(isScanning){
				try{
					c.autoFocus(MoviePreview.this);
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		}
	}
}
